/*
*    Clean AJAX Engine v4.0
*    Copyright (C) 2005-2006 Carlos Eduardo Goncalves (cadu.goncalves@gmail.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program; if not, write to the Free Software
*    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/**
* Class that provides a tiny AJAX engine.
* @author Carlos Eduardo Goncalves
*/

/** 
* <p>Nano AJAX engine class constructor.</p>
* @param debug 
*		<code>Boolean</code> flag that turns on/off the engine debug mode.
*/
function NanoEngine(debug){
  /** <p>Debug mode flag.</p> */	
  NanoEngine.debug = debug; 
  if(NanoEngine.debug)
    Console.start();  
}

/** <p>Array that holds all active messages.</p> */
NanoEngine.messages = new Array();		

/** <p>ActiveX classes that provide XMLHttpRequest features.</p> */
NanoEngine.ACTIVEX_XHR = ["Microsoft.XMLHTTP", "MSXML2.XMLHTTP"];

/** <p>HTTP supported methods.</p> */
NanoEngine.HTTP_METHODS = ["GET", "POST"];

/** <p>HTTP status codes.</p> */
NanoEngine.HTTP_STATUS_CODES = ["200 OK", "201 Created", "202 Accepted", "203 Non-Authoritative Information",
								"205 Reset Content", "204 No Content", "206 Partial Content",
								"300 Multiple Choices", "301 Moved Permanently", "302 Found", "303 See Other",
								"304 Not Modified", "305 Use Proxy", "307 Temporary Redirect",
								"400 Bad Request", "401 Unauthorized", "402 Payment Required", 
								"403 Forbidden",  "404 Not Found", "405 Method Not Allowed", "406 Not Acceptable",
								"407 Proxy Authentication Required", "408 Request Timeout", "409 Conflict",
								"410 Gone", "411 Length Required", "412 Precondition Failed", 
								"413 Request Entity Too Large", " 414 Request-URI Too Long", "415 Unsupported Media Type",
								"416 Requested Range Not Satisfiable", "417 Expectation Failed",
								"500 Internal Server Error", "501 Not Implemented", "502 Bad Gateway", 
								"503 Service Unavailable", "504 Gateway Timeout", "505 HTTP Version Not Supported"];

/** 
* <p>Define the way how exceptions are reported.</p>
* @param msg_id
*		Unique identifier for a message on messages array.
* @param exception
*		Exception to be displayed or threw.
* @throws Run time exception.
*/
NanoEngine.reportException = function(msg_id, exception){
  if(NanoEngine.debug)
    Console.trace(exception, Console.EXCEPTION);
  else{		
    var msg = NanoEngine.messages[msg_id];
    if((msg == null) || (msg.onError == null))
      throw exception;
    else
      msg.onError(exception);	  
  }
  NanoEngine.removeMessage(msg_id);	  
}

/** 
* <p>Remove an AJAX message from the queue.</p>
* @param msg_id
*		Unique identifier for a message on messages array.
*/
NanoEngine.removeMessage = function(msg_id){
  if(isNaN(msg_id)) 
    return;
  if(NanoEngine.messages.length > msg_id)
    NanoEngine.messages[msg_id] = null;
}

/** 
* <p>Try to build an ActiveX object based on a list classes.</p>
* @param names
*		Array with ActiveX class names that could be created.
* @return
*		Reference to an <code>ActiveXObject</code>.
*/
NanoEngine.buildActiveX = function(names){
  var obj = null;
  for(var i = 0; i < names.length; i++) {
    try {
      obj = new ActiveXObject(names[i]);
	  break;
     } catch (e) {/* Ignore the exception and try the next */}
  }
  return obj;
}

/** 
* <p>Build client side JavaScript objects required to XMLHTTP request.</p>
* @return
*		Reference to a <code>XMLHttpRequest</code> or <code>ActiveXObject</code> instance.
*/
NanoEngine.buildRequest = function (){
  try{
    var obj = (typeof XMLHttpRequest != "undefined") ? new XMLHttpRequest() : NanoEngine.buildActiveX(NanoEngine.ACTIVEX_XHR);		
	return obj;
  } catch(e){NanoEngine.reportException(null, e);}	
}

/**
* <p>Create a message setting all properties.</p>
* @param url
*		Url to access.
* @param consumer
*		Consumer id.
* @param onError
*		Function that should be called at the onError event.
* @param onChange
*		Function that should be called at the onChange event.
* @param onComplete
*		Function that should be called at the onComplete event.
* @return
*  		New message instance.
*/
NanoEngine.createMessage = function(url, consumer, onError, onChange, onComplete){
  var message = {};
  message.id = null;	
  message.method = "";
  message.value = null;  
  message.address = url;
  message.consumer = consumer;
  message.onError = onError; 
  message.onChange = onChange;   
  message.onComplete = onComplete;   
  return message;
} 

/** 
* <p>Send a message after add it to messages array.</p>
* @param msg
*		Object that represent the message to send.
* @return 
*		Unique id generated by the engine for the message.
* @see Connection.sendMessage(Message)
*/
NanoEngine.sendMessage = function(msg){
  try{
    var wrapper = NanoEngine.wrapMessage(msg);	 
	if(msg.method.toUpperCase() == "GET")
	  wrapper.request.setRequestHeader("If-Modified-Since", "Tue, 1 Jan 1980 00:00:00 GMT");
    wrapper.request.send(msg.value);  
    Console.trace(msg, Console.REQUEST);		
	return wrapper.id; 	
  } catch(e){NanoEngine.reportException(msg.id, e);}
}


/** 
* <p>Import all AJAX message data to the wrapper.</p>
* @param msg
*		Object that represents the message to be wrapped.	
* @return
*  		Object that represents the message wrapper.
* @see MessageWrapper.wrap(Message)
*/
NanoEngine.wrapMessage = function(msg){
  try{		
	if(NanoEngine.HTTP_METHODS.toString().indexOf(msg.method.toUpperCase()) == -1)
	  msg.method = "POST";		  
	var wrapper = {}; 
    wrapper.id = NanoEngine.messages.length;   
	wrapper.consumer = msg.consumer; 
	wrapper.address = msg.address;	
    wrapper.request = NanoEngine.buildRequest();
    wrapper.request.open(msg.method, msg.address);
    wrapper.onError = msg.onError;
    wrapper.onComplete = msg.onComplete;	
    if(msg.onChange != null)
	  wrapper.request.onreadystatechange = msg.onChange;   
    else{ 
	  wrapper.onChange = function() {
		  try{
			if(this.request.readyState == 4){	
              var s = NanoEngine.parseStatus(this.request.status)  + ": " + this.address;
              Console.trace(s, Console.RESPONSE);			
			  if(this.request.status >= 200 && this.request.status <= 299){
				var el = document.getElementById(this.consumer);
				if(el.innerHTML != null)
				   el.innerHTML = this.request.responseText;			  
			  }
			  if(this.onComplete != null)
				this.onComplete(this.request);  			  
			  NanoEngine.removeMessage(this.id);		
			}
		  } catch(e){NanoEngine.reportException(null, e);}  
  	  }	
	  var _this = wrapper;
	  var args = [wrapper];
	  wrapper.request.onreadystatechange = function() {
        _this.onChange.apply(args[0], args);
  	  }
    }
	NanoEngine.messages.push(wrapper);
	return wrapper;
  } catch(e){NanoEngine.reportException(null, e);}
}

/**
* <p>Return user friendly HTTP status code.</p>
* @param status
*		Status code.
* @return
*		<code>String</code> with the request status and its definition.
*/
NanoEngine.parseStatus = function(status){ 
  try{
    var str = "HTTP status " + status;
    for(var i = 0; i < NanoEngine.HTTP_STATUS_CODES.length; ++i){
      if(NanoEngine.HTTP_STATUS_CODES[i].indexOf(status) != -1){
	    str = NanoEngine.HTTP_STATUS_CODES[i];	
	    break;
	  }
    }
    return str;
  } catch(e){NanoEngine.reportException(null, e);}    
}